# -*- coding: utf-8 -*-
import numpy as np
import geatpy as ea
import pandas as pd
import math
import matlab.engine
# from ga_method import *

eng = matlab.engine.start_matlab()
def test_pz(Hs, rate,flag):
    if flag ==1:
        print(flag)
    # 计算单个直方图分别在0-254喝255-512中大于0的点的个数
    # Left = np.sum(Hs[0:254] > 0)  # -255-0
    # Right = np.sum(Hs[255:512] > 0)  # 256-512
    # pp = np.zeros((1, Left))#记录1-256中非0的元素
    # nps = np.zeros((1, Right))#记录-255-0中非0元素的坐标
    # pz = np.zeros((1, 255 - len(pp)))#记录1-256中0的元素
    # nz = np.zeros((1, 255 - len(nps)))#记录-255-0中0元素的坐标
    nps = np.where(Hs[0:255] != 0)[0]  # 记录1-256中非0的元素
    nz = np.where(Hs[0:255] == 0)[0]  # 记录1-256中0的元素
    pp = np.where(Hs[255:512] != 0)[0]  # 记录-255-0中非0元素的坐标
    pz = np.where(Hs[255:512] == 0)[0]  # 记录-255-0中0元素的坐标
    nps = -np.sort(-nps)
    nz = -np.sort(-nz)
    print('nps',len(nps))
    print('pp',len(pp))
    maxT = 0  # 最近的0值点
    minT = 0
    if 255 - nz[0] < pz[0]:
        maxT = 254 - nz[0]
    else:
        maxT = pz[0]
    s = Hs[255]
    # 找到第一个满足分配容量的点
    for i in range(maxT):
        if s >= rate:
            break
        if i > 0:
            s = s + Hs[255 + i] + Hs[255 - i]
            minT = i
    if minT == maxT + 1:
        P = 0
        Z = 0
        return P, Z
    tmp = 0

    while Hs[tmp] == 0 :
        if tmp<510:
            tmp += 1
        else:
            break
    print('asdas',tmp)
    nz = np.arange(tmp, 0, -1)

    tmp = 510
    while Hs[tmp] == 0:
        if tmp>0:
            tmp -= 1
        else:
            break
    pz = np.arange(tmp + 1, 511, 1)
    tmd = 100000000

    P = []
    Z = []
    Rate = 0
    D = 0
    for t in range(minT, maxT):
        R = 0
        D = 0
        i = t
        tP = []
        tZ = []
        while R < rate:
            R = R + Hs[i + 255]
            if i >= 0:
                tP.append(pp[i] + 255)
                if t - i > len(pz):
                    P = [254, -254]
                    Z = [255, -255]
                    return
                tZ.append(pz[t - i])
                i = -i
            else:
                tP.append(nps[-i - 1])
                if t + i > len(nz):
                    P = [254, -254]
                    Z = [255, -255]
                    return
                # print(t + i+1)
                # print(nz[t + i],nz[t + i+1],nz[t + i-1])
                tZ.append(nz[t + i+1])
                i = -i - 1

        R, D = rateAndDist(tP, tZ, Hs)
        if D < tmd:
            tmd = D
            P = tP
            Z = tZ
            Rate = R
    print('p,z,r,d', np.array(P) - 255, np.array(Z) - 255, Rate, tmd)
    return P, Z

def rateAndDist(Ps, Zs, H):
    # print('p', Ps)
    m = len(Ps)
    n = len(H)
    Ps = np.array(Ps, dtype=int)
    Zs = np.array(Zs, dtype=int)
    C = np.zeros((1, n), dtype='double')
    PK = np.zeros((1, n), dtype='double')
    sig = np.sign(Zs - Ps)
    PK[0][Ps] = sig
    # print(PK,Ps,sig,Zs)
    for i in range(m):
        # print(Ps[i] + sig[i], Zs[i] - sig[i] + 1, sig[i])
        tem = np.arange(Ps[i] + sig[i], Zs[i] - sig[i] + 1, sig[i])
        C[0, tem] += sig[i]
    rates = sum(H[Ps])
    # print(C)
    # print(rates)
    H = H.reshape(511, -1)
    dists = 0.5 * (np.sum(np.dot((np.square(C) + np.square(C + PK)), H)))
    # print(rates, dists)
    return rates, dists


class MyProblem(ea.Problem):  # 继承Problem父类
    def __init__(self, data,rates):
        name = 'MyProblem'  # 初始化name（函数名称，可以随意设置）
        M = 1  # 初始化M（目标维数）
        maxormins = [1]  # 初始化maxormins（目标最小最大化标记列表，1：最小化该目标；-1：最大化该目标）
        Dim = len(data[0])  # 初始化Dim（决策变量维数）
        varTypes = [1] * Dim  # 初始化varTypes（决策变量的类型，元素为0表示对应的变量是连续的；1表示是离散的）
        lb = [0] * Dim  # 决策变量下界
        ub = [1] * Dim  # 决策变量上界
        lbin = [1] * Dim  # 决策变量下边界（0表示不包含该变量的下边界，1表示包含）
        ubin = [1] * Dim  # 决策变量上边界（0表示不包含该变量的上边界，1表示包含）
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)
        self.P = data[0]
        self.Z = data[1]
        self.H = data[2]
        self.ec = rates

    def aimFunc(self, pop):  # 目标函数
        Vars = pop.Phen  # 得到决策变量矩阵
        x = Vars.astype(int)  # 得到决策变量矩阵
        # print("x", x.shape)
        # print(Vars.shape)
        # x1 = Vars[:, [0]]
        P = self.P
        Z = self.Z
        H = self.H
        P = P * x
        Z = Z * x
        # print('pppp',P.shape)
        dist = []
        Rate = []
        for i in range(len(x)):
            temp1 = P[i].copy()
            temp2 = Z[i].copy()
            temp1 = temp1[temp1 != 0]
            temp2 = temp2[temp2 != 0]
            # print('bbb',P[i],Z[i])
            # print('aaaa',temp1,temp2)
            R, D = rateAndDist(temp1, temp2, H)
            dist.append(D)
            Rate.append(R)

        dist = np.array(dist)
        # mx = np.argmax(dist)
        # dist = mx - dist
        Rate = np.array(Rate)
        # print('sdasd',dist,Rate)
        # size = dist.shape[0]
        # dist= dist.reshape(size,-1)
        # print('xxxxx',np.array([Rate]).T-6654)
        # idx2 = np.where(Rate < 6654)[0]
        # dist[idx2] = 9999999999999
        pop.CV = self.ec - np.array([Rate]).T
        pop.ObjV = np.vstack([dist]).T  # 计算目标函数值，赋值给pop种群对象的ObjV属性

    # def calReferObjV(self):  # 设定目标数参考值（本问题目标函数参考值设定为理论最优值）
    #     referenceObjV = np.array([[2.5]])
    #     return referenceObjV


def jijue(data,rates):
    """===============================实例化问题对象==========================="""
    problem = MyProblem(data,rates)  # 生成问题对象
    """=================================种群设置==============================="""
    Encoding = 'BG'  # 编码方式
    NIND = 100  # 种群规模
    Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders)  # 创建区域描述器
    population = ea.Population(Encoding, Field, NIND)  # 实例化种群对象（此时种群还没被初始化，仅仅是完成种群对象的实例化）
    """===============================算法参数设置============================="""
    myAlgorithm = ea.soea_SEGA_templet(problem, population)  # 实例化一个算法模板对象
    myAlgorithm.mutOper.Pm = 0.2  # 修改变异算子的变异概率
    myAlgorithm.recOper.XOVR = 0.8  # 修改交叉算子的交叉概率
    myAlgorithm.MAXGEN = 100  # 最大进化代数
    """==========================调用算法模板进行种群进化======================="""
    [NDSet, population] = myAlgorithm.run()  # 执行算法模板，得到非支配种群以及最后一代种群
    NDSet.save()  # 把非支配种群的信息保存到文件中
    """==================================输出结果=============================="""
    print('用时：%s 秒' % myAlgorithm.passTime)
    print('最有个体', NDSet.ObjV)
    print('值', NDSet.Phen)
    print('非支配个体数：%d 个' % NDSet.sizes)
    # print(NDSet.Phen[0]*P)
    if NDSet.sizes != 0:
        Px = NDSet.Phen[0] * data[0]
        Zx = NDSet.Phen[0] * data[1]
        Px = Px[Px != 0]
        Zx = Zx[Zx != 0]
        data = [Px, Zx, data[2]]
        print('此时的参数', Px, Zx)
        return data, NDSet.ObjV
    else:
        print('没有找到可行解！')
        return data, 100000000


def get_h(H, rates,flag):
    P, Z = test_pz(H, rates,flag)
    return [P, Z, H]


# def get_h2(H,rate):
#     # H = np.array(H['H'][0])
#     # nps = np.where(H[0:255] != 0)[0]  # 记录-255-0中非0的元素
#     # nz = np.where(H[0:255] == 0)[0]  # 记录-255-0中0的元素
#     # pp = np.where(H[255:512] != 0)[0]  # 记录1-256中非0元素的坐标
#     # pz = np.where(H[255:512] == 0)[0]  # 记录1-256中0元素的坐标
#     # nps = -np.sort(-nps)
#     # nz = -np.sort(-nz)
#     # nz = nz[0:len(nps)]
#     # pz = pz[0:len(pp)]
#     # pp = pp + 255
#     # pz = pz + 255
#     # P = np.append(nps, pp)
#     # Z = np.append(nz, pz)
#     P,Z = test_pz(H,rate)
#     return [P,Z,H]


def make_hs(erro, percent):
    print(int(np.floor(percent * len(erro) * 0.01)))
    erro = erro[0:int(np.ceil(percent * len(erro) * 0.01))]
    length = len(erro)
    H = np.zeros(511)
    for i in range(length):
        H[int(erro[i] + 255)] += 1
    return H


def Ga_one(errs, percent, dist_lists, rates,flag):
    H = make_hs(errs, percent)
    datas = get_h(H, rates,flag)
    datas, dist = jijue(datas,rates)
    print(dist)
    # if dist<dist_list[int(percent)]:
    dist_lists[int(percent)] = dist

    return dist_lists, datas


# def Ga_one2(err,percent,dist_list,rate):
#     H = make_hs(err,percent)
#     data= get_h2(H,rate)
#     data,dist=jijue(data)
#     if dist<dist_list[int(percent)]:
#         dist_list[int(percent)] = dist
#     return dist_list

# if __name__ == '__main__':
#     # P = np.array([3, -3, 2, -2, 1, -1, 0]) + 255
#     # Z = np.array([15, -15, 16, -16, 17, -17, 18]) + 255
#     P = []
#     Z = []
#     rate = 6576
#     dist_list = np.ones(100) * 100000000
#     pl_size = np.zeros((100, 2))
#     tmpArea = np.zeros((100, 2))
#     Dist = 10000000000
#     err = eng.readdata('sorted_errors2.mat')
#     # print(err)
#     err = np.array(err['sorted_errors'][0])
#     miner = np.ceil(rate / len(err) * 100)
#     maxer = 99
#     dist_list, data = Ga_one(err, miner, dist_list, rate)
#     if dist_list[int(miner)] != 0 and dist_list[int(miner)] < Dist:
#         Dist = dist_list[int(miner)]
#         P = data[0]
#         Z = data[1]
#
#     dist_list, data = Ga_one(err, maxer, dist_list, rate)
#     if dist_list[int(maxer)] != 0 and dist_list[int(maxer)] < Dist:
#         Dist = dist_list[int(maxer)]
#         P = data[0]
#         Z = data[1]
#     temp = 1
#     pl_size[0, :] = [miner, maxer]
#     if miner + 1 == maxer:
#         temp = 0
#     index = 0
#     aa = []
#     while temp > 0:
#         tmpC = 0
#         for i in range(temp):
#             # print('plsize',pl_size)
#             idx = np.floor((pl_size[i, 0] + pl_size[i, 1]) / 2)
#             aa.append([ idx, pl_size[i, 0], pl_size[i, 1]])
#             print('这次的', idx, pl_size[i, 0], pl_size[i, 1])
#             dist_list, data = Ga_one(err, idx, dist_list, rate)
#             if dist_list[int(idx)] != 0 and dist_list[int(idx)] < Dist:
#                 Dist = dist_list[int(idx)]
#                 P = data[0]
#                 Z = data[1]
#             if idx > pl_size[i, 0] + 1:
#                 # print('tmc1',tmpC)
#                 tmpArea[tmpC, :] = [pl_size[i, 0], idx]
#                 tmpC += 1
#             if idx + 1 < pl_size[i, 1]:
#                 print('tmc2', tmpC)
#                 tmpArea[tmpC, :] = [idx, pl_size[i, 1]]
#                 tmpC += 1
#         temp = 0
#         minindex = np.argmin(dist_list)
#         # print('midx',minindex)
#         # print('tmpArea', tmpArea)
#         for i in range(tmpC):
#             if tmpArea[i, 0] == minindex or tmpArea[i, 1] == minindex:
#                 pl_size[temp, :] = tmpArea[i, :]
#                 temp += 1

    # Ga_one(err, test, dist_list,rate)
    # dist_list, data = Ga_one(err, 29, dist_list, rate)
    # H = make_hs(err, 29)
    # file_names = 'HHH.mat'
    # scio.savemat(file_names, {'HHH': H})
    # print(H)
    # tP = [257,253]
    # tZ = [287,236]
    # R, D = rateAndDist(tP, tZ, H)
    # print(D)
    # test_pz(H,rate)
    #
    # print('asd', data[0])
    # test = np.argmin(dist_list)
    #
    # print('dsfdsfg', dist_list)
    # print('asdasdasd', test)
    # print('此时pz的值', P, Z, Dist)
    # print(aa)
    #
